﻿using Core.DataAccess.Model;
using Core.Framework;
using Core.Framework.Common;
using Grain.Interfaces;
using iTool.ClusterComponent;
using Local.Implement;
using Local.Interface;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Silo.CloudService;
using Silo.SocketService;
using SocketService;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace iTool.Service
{
    public class MainProvider
    {
        private iToolClusterHostServer clusterHostServer;

        private IHost pluginHosting;
        private SubscribeStreamProvider streamHandlerInstallpackage;
        private SubscribeStreamProvider streamHandlerScriptBudiler;
        private SubscribeStreamProvider streamHandlerScriptDelete;

        private readonly ManualResetEvent manual;
        private readonly string _iToolServiceProcessPath = "/var/iTool/service.process";
        private readonly string _commandPath = "./command.sh";
        private readonly string _iToolStartTypePath = "/var/iTool/local.s";
        const string gatewaydatabasePath = "../gatewaydatabase.config";
        private string _PublicIp { get; set; }

        private string PublicIp
        {
            get
            {
                if (string.IsNullOrWhiteSpace(_PublicIp))
                {
#if DEBUG
                    _PublicIp = "127.0.0.1";
#else
                    _PublicIp = Executer.Run("cat /var/iTool/publicip.s");
#endif

                    if (string.IsNullOrWhiteSpace(_PublicIp))
                    {
                        _PublicIp = Executer.Run("curl www.icanhazip.com > /var/iTool/publicip.s");
                    }

                    if (!string.IsNullOrWhiteSpace(_PublicIp))
                    {
                        _PublicIp = _PublicIp.Trim().TrimEnd('\r').TrimEnd('\n').TrimEnd('\r');
                    }
                }

                return _PublicIp;
            }
        }

        public MainProvider(ManualResetEvent manual)
        {
            this.manual = manual;
#if DEBUG
            _iToolServiceProcessPath = $"D:/iTool-Cloud-Framework/service.process";
            //_iToolServiceProcessPath = $"{Directory.GetCurrentDirectory()}/service.process";
                _iToolStartTypePath = "D:/iTool-Cloud-Framework/local.s";
#elif CLOUD
            _iToolServiceProcessPath = $"D:/iTool-Cloud-Framework/service.process";
#endif
            if (!File.Exists(_commandPath))
            {
                File.Create(_commandPath).Close();
                Executer.Run("chmod u+x " + _commandPath);
            }

        }

        public async Task StartAsync()
        {
            Print.Line($"iTool> Hello World!", ConsoleColor.Cyan);
            Print.Line($"iTool> Service Port:{AppSetting.iToolConfigure.ServerPort}", ConsoleColor.Cyan);
            Print.Line($"iTool> Service Gateway Port:{AppSetting.iToolConfigure.ServerGatewayPort}", ConsoleColor.Cyan);

            try
            {
                // Write State
                this.WriteProcessState(ProcessStatusEnum.Staring);

                // 2. Create Plugin Support
                this.pluginHosting = PluginsHttpHostHandel.Create();

                // 3. Start Hosting
                iToolServiceProvider iToolServiceProvider = new iToolServiceProvider();
                this.clusterHostServer = await iToolServiceProvider.StartAsync();

                // 4. Build Service Provider
                await this.BuildServiceCollection();

                // 5. Check node_module
                var nodePackageProvider = this.GetService<NodePackageProvider>();
                Print.Line("\niTool> Check node install package...", ConsoleColor.DarkYellow);
                nodePackageProvider.CheckNotInstallPackage();
                Print.Line("iTool> Check node install package success!", ConsoleColor.Green);

                // 6. Reg Consumer
                var iToolConsumer = this.GetService<iToolConsumerProvider>();
                if (!await this.SubscribeStreamAsync(iToolConsumer))
                {
                    this.WriteProcessState(ProcessStatusEnum.StartError);
                    manual.Set();
                }
                else
                {
                    this.WriteProcessState(ProcessStatusEnum.Started);
                }

                // 7. Set DefaultDB
                // 检查是否是一键安装 【本地文件】 if
                if (File.Exists(_iToolStartTypePath))
                {
                    try
                    {
                        if (File.Exists(gatewaydatabasePath))
                        {
                            var iDatabaseConfiguration = this.clusterHostServer.GetService<IDatabaseConfigurationService>();
                            var value = File.ReadAllText(gatewaydatabasePath);
                            var dbParameter = Newtonsoft.Json.JsonConvert.DeserializeObject<CreateDataBaseRequest>(value);
                            var dbConnaction = await iDatabaseConfiguration.GetConnactionByTitle("Default");
                            if (string.IsNullOrWhiteSpace(dbConnaction.connationString))
                            {
                                await iDatabaseConfiguration.Register("Default", $"Data Source={this.PublicIp},{dbParameter.Port};database=CustomData;uid=sa;pwd={dbParameter.Passward};MultipleActiveResultSets=true;");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Set DefaultDB {DateTime.Now} ，{ex.Message}！\n");
                    }
                }

            }
            catch (Exception ex)
            {
                CLog.WriteLineError(ex.Message);
                CLog.WriteLineError(ex.StackTrace);
                this.StopSystemctl();
                this.WriteProcessState(ProcessStatusEnum.StartError);
                manual.Set();
            }
        }

        public async Task ReStartAsync()
        {
            Console.WriteLine($"{DateTime.Now} ，等待停止信号！\n");
            this.WriteProcessState(ProcessStatusEnum.WaitingStopSignal);

            // 等待停止信号
            var fileChanged = new iToolFileChangeDetect(_iToolServiceProcessPath);
            fileChanged.StartWaitResult(value => value == ProcessStatusEnum.BeginStop.ToString(), 240 * 1000);

            TimerJob.Stop();
            await this.RandomDelay();
            await this.StopAsync(true);

            // 清空服务
            ProviderOfServices.Clear();

            // GC
            GC.Collect();

            Console.WriteLine($"{DateTime.Now} ，等待启动信号！\n");
            this.WriteProcessState(ProcessStatusEnum.WaitingStartSignal);

            // 等待启动信号
            var fileChanged2 = new iToolFileChangeDetect(_iToolServiceProcessPath);
            fileChanged2.StartWaitResult(value => value == ProcessStatusEnum.BeginStart.ToString(), 10 * 1000);
            LoggerUtils.Write($"call client start", "log_service");

            await this.RandomDelay(3333);
            await this.StartAsync();
        }

        public async Task StopAsync(bool isReStart = false)
        {
            if (this.clusterHostServer == null)
                return;

            Console.WriteLine($"{DateTime.Now} ，准备进行资源清理！");
            try
            {
                this.WriteProcessState(ProcessStatusEnum.Stoping);

                // 1. Un subscribe
                await streamHandlerInstallpackage.UnsubscribeAsync();
                await streamHandlerScriptBudiler.UnsubscribeAsync();
                await streamHandlerScriptDelete.UnsubscribeAsync();

                // 清空服务
                ProviderOfServices.Clear();

                // 2. Stop Service
                await this.clusterHostServer.StopAsync();

                // 3. Stop Plugin
                await this.pluginHosting.StopAsync();
                this.pluginHosting.Dispose();

                if (!isReStart)
                {
                    // 4. Stop System 否则会自动重启
                    this.StopSystemctl();

                    Console.WriteLine($"{DateTime.Now} ，服务进程已正常退出！");
                    manual.Set();
                }
            }
            finally
            {
                this.WriteProcessState(ProcessStatusEnum.Stoped);
            }
        }


        private async Task<bool> SubscribeStreamAsync(iToolConsumerProvider iToolConsumer)
        {

            Print.Line("\niTool> Consumer starting...", ConsoleColor.DarkYellow);

            try
            {
                streamHandlerInstallpackage = new SubscribeStreamProvider(AppSetting.INSTALLPACKAGETOPIC);
                streamHandlerInstallpackage.Handler += (sender, message) => iToolConsumer.ExecuteInstallPackage(message);
                await streamHandlerInstallpackage.StartAsync();

                streamHandlerScriptBudiler = new SubscribeStreamProvider(AppSetting.ScriptBudilerTopic);
                streamHandlerScriptBudiler.Handler += (sender, message) => iToolConsumer.ExecuteScriptBudiler(message);
                await streamHandlerScriptBudiler.StartAsync();

                streamHandlerScriptDelete = new SubscribeStreamProvider(AppSetting.ScriptDeleteTopic);
                streamHandlerScriptDelete.Handler += (sender, message) => iToolConsumer.ExecuteScriptDelete(message);
                await streamHandlerScriptDelete.StartAsync();

                Print.Line("iTool> Consumer success.", ConsoleColor.Green);

                return true;
            }
            catch (Exception ex)
            {
                Print.Error($"iTool> Consumer error {ex.TryToJson()}.");
                return false;
            }

        }

        private async Task<IServiceCollection> BuildServiceCollection()
        {
            IServiceCollection services = ProviderOfServices.GetServiceCollection();
            services.AddSingleton<IDBFactory, SQLServer>();
            services.UseiToolLogger();
            services.AddSingleton<INodeInstallPackage, NodeInstallPackage>();
            services.AddSingleton<InstallPackageTimerJob>();
            services.AddSingleton<iToolConsumerProvider>();
            services.AddSingleton<NodePackageProvider>();
            await Task.Delay(100);
            return services;
        }

        private T GetService<T>() where T : class
        {
            return ProviderOfServices.GetService<T>();
        }

        private void WriteProcessState(ProcessStatusEnum processStatus)
        {
            if (!File.Exists(_iToolServiceProcessPath))
            {
                File.Create(_iToolServiceProcessPath).Close();
            }

            File.WriteAllText(_iToolServiceProcessPath, processStatus.ToString());
        }

        private async Task RandomDelay(int max = 666)
        {
            // 制造错峰
            await Task.Delay(new Random().Next(1, max));
        }


        private void StopSystemctl()
        {
            string stopiToolServer = "systemctl stop iToolServer.service";
            File.WriteAllText(_commandPath, stopiToolServer);
            Executer.Run("sh " + _commandPath);
        }
    }



    public class CreateDataBaseRequest
    {
        public int Port { get; set; }
        public string Passward { get; set; }
    }
}
